/*
 * OperationExpression.cpp
 *
 *  Created on: 01.04.2014
 *      Author: Nicolas Winkler
 */

#include "OperationExpression.h"

#include <iostream>
#include <sstream>
#include <algorithm>

using namespace solver;

static std::string equationSymbol[] =
{ "==", "=" };
static std::string addSymbols[] =
{ "+", "-" };
static std::string multSymbols[] =
{ "*", "/" };
static std::string powerSymbols[] =
{ "^" };


const TermTemplate TermTemplate::power(powerSymbols,
		sizeof powerSymbols / sizeof(std::string));

const TermTemplate TermTemplate::multiplication(multSymbols,
		sizeof multSymbols / sizeof(std::string), &power);

const TermTemplate TermTemplate::addition(addSymbols,
		sizeof addSymbols / sizeof(std::string), &multiplication);

const TermTemplate TermTemplate::equation(equationSymbol,
		sizeof equationSymbol / sizeof(std::string), &addition);


TermTemplate::TermTemplate(void)
{
	subTemplate = 0;
}


TermTemplate::TermTemplate(std::string* operators, unsigned int nOperators)
{
	subTemplate = 0;
	for (unsigned int i = 0; i < nOperators; i++)
	{
		this->operators.push_back(operators[i]);
	}
}


TermTemplate::TermTemplate(std::string* operators, unsigned int nOperators,
		const TermTemplate* subTemplate)
{
	this->subTemplate = subTemplate;
	for (unsigned int i = 0; i < nOperators; i++) {
		this->operators.push_back(operators[i]);
	}
}


bool TermTemplate::operatorMatches(const std::string& op) const
{
	for (unsigned int i = 0; i < operators.size(); i++) {
		if (op == operators[i])
			return true;
	}
	return false;
}


std::string TermTemplate::toString(void) const
{
	std::stringstream str;
	str << "nOperators: " << operators.size();
	return str.str();
}


Expression* TermTemplate::createTermExpression(const TermTokenBlock& ttb) const
{
#ifdef DEBUG_MESSAGES
	std::cout << "create: " << ttb.toString() << "\n";
#endif

	using namespace std;
	string operatorTxt;
	bool foundOperator = false;

	vector<Operation> left;
	vector<Operation> right;

	for (int i = ttb.getNOperations() - 1; i >= 0; i--) {
		const Operation& op = ttb.getOperation(i);
		if (!foundOperator) {
			right.push_back(op);
		}
		else {
			left.push_back(op);
		}
		if (!foundOperator && i > 0 && operatorMatches(op.operatorToken)) {
			foundOperator = true;
			operatorTxt = op.operatorToken;
		}
	}

	std::reverse(right.begin(), right.end());
	std::reverse(left.begin(), left.end());

	if (!foundOperator) {
		if (right.size() > 1 && subTemplate != 0) {
			return subTemplate->createTermExpression(ttb);
		}
		else if (right.size() == 1) {
			const StringTokenBlock* stb = dynamic_cast
								<const StringTokenBlock*>(right[0].operand);
			const TermTokenBlock* ttb = dynamic_cast
								<const TermTokenBlock*>(right[0].operand);
			if (stb != 0) {
				return new IntegerExpression(::atoll(stb->getToken().c_str()));
			}
			else if (ttb != 0) {
				const TermTemplate& temp = TermTemplate::getBaseTermTemplate();
				return temp.createTermExpression(*ttb);
			}
		}
		return 0;
	}

	Expression* leftExpr = 0;
	Expression* rightExpr = 0;

	if (right.size() > 1) {
		if (subTemplate != 0) {
			rightExpr = subTemplate->createTermExpression(TermTokenBlock(right));
		}
	}
	else if (right.size() == 1) {
		const StringTokenBlock* stb = dynamic_cast
				<const StringTokenBlock*> (right[0].operand);
		const TermTokenBlock* ttb = dynamic_cast
				<const TermTokenBlock*>(right[0].operand);

		if (stb != 0) {
			const string& val = stb->getToken();
			rightExpr = new IntegerExpression(::atoll(val.c_str()));
		} else if (ttb != 0) {
			const TermTemplate& temp = TermTemplate::getBaseTermTemplate();
			rightExpr = temp.createTermExpression(*ttb);
		}
	}

	leftExpr = createTermExpression(TermTokenBlock(left));

	if (operatorTxt == "+") {
		return new AdditionExpression(leftExpr, rightExpr);
	}
	else if (operatorTxt == "-") {
		return new SubtractionExpression(leftExpr, rightExpr);
	}
	else if (operatorTxt == "*") {
		return new MultiplicationExpression(leftExpr, rightExpr);
	}
	else if (operatorTxt == "/") {
		return new DivisionExpression(leftExpr, rightExpr);
	}
	else if (operatorTxt == "^") {
		return new PowerExpression(leftExpr, rightExpr);
	}

	return 0;
}


const TermTemplate& TermTemplate::getEquation(void)
{
	return TermTemplate::equation;
}

const TermTemplate& TermTemplate::getAddition(void)
{
	return TermTemplate::addition;
}

const TermTemplate& TermTemplate::getMultiplication(void)
{
	return TermTemplate::multiplication;
}

const TermTemplate& TermTemplate::getPower(void)
{
	return TermTemplate::power;
}


const TermTemplate& TermTemplate::getBaseTermTemplate(void)
{
	return getEquation();
}


TermExpression::TermExpression(void)
{
	a = 0;
	b = 0;
}


TermExpression::~TermExpression(void)
{
}


std::string TermExpression::getAsString(void) const
{
	std::string a_str;
	std::string b_str;

	if (dynamic_cast<const IntegerExpression*> (a) != 0)
		a_str = a->getAsString();
	else
		a_str = "(" + a->getAsString() + ")";

	if (dynamic_cast<const IntegerExpression*> (b) != 0)
		b_str = b->getAsString();
	else
		b_str = "(" + b->getAsString() + ")";

	return a_str + " " + getOperatorSymbol() + " " + b_str;
}


std::string TermExpression::toString(void) const
{
	return "Operation[" + a->toString() + " " + getOperatorSymbol() + " " + b->toString() + "]";
}


const std::string AdditionExpression::operatorSymbol = "+";
AdditionExpression::AdditionExpression(void)
{
}


AdditionExpression::AdditionExpression(const Expression* a, const Expression* b)
{
	this->a = a;
	this->b = b;
}


AdditionExpression::~AdditionExpression(void)
{
}


const Expression* AdditionExpression::evaluate(void) const
{
	const Expression* aEval = a->evaluate();
	const Expression* bEval = b->evaluate();

	const IntegerExpression* aVal = dynamic_cast
			<const IntegerExpression*> (aEval);
	const IntegerExpression* bVal = dynamic_cast
		<const IntegerExpression*> (bEval);
	if (aVal != 0 && bVal != 0) {
		return new IntegerExpression(aVal->value + bVal->value);
	}

	return aEval->add(bEval);
}


const std::string& AdditionExpression::getOperatorSymbol(void) const
{
	return operatorSymbol;
}



const std::string SubtractionExpression::operatorSymbol = "-";
SubtractionExpression::SubtractionExpression(void)
{
}


SubtractionExpression::SubtractionExpression(const Expression* a, const Expression* b)
{
	this->a = a;
	this->b = b;
}


SubtractionExpression::~SubtractionExpression(void)
{
}


const Expression* SubtractionExpression::evaluate(void) const
{
	const Expression* aEval = a->evaluate();
	const Expression* bEval = b->evaluate();

	const IntegerExpression* aVal = dynamic_cast
			<const IntegerExpression*> (aEval);
	const IntegerExpression* bVal = dynamic_cast
		<const IntegerExpression*> (bEval);
	if (aVal != 0 && bVal != 0) {
		return new IntegerExpression(aVal->value - bVal->value);
	}

	return aEval->sub(bEval);
}


const std::string& SubtractionExpression::getOperatorSymbol(void) const
{
	return operatorSymbol;
}


const std::string MultiplicationExpression::operatorSymbol = "*";
MultiplicationExpression::MultiplicationExpression(void)
{
}


MultiplicationExpression::MultiplicationExpression(const Expression* a, const Expression* b)
{
	this->a = a;
	this->b = b;
}


MultiplicationExpression::~MultiplicationExpression(void)
{
}


const Expression* MultiplicationExpression::evaluate(void) const
{
	const Expression* aEval = a->evaluate();
	const Expression* bEval = b->evaluate();

	const IntegerExpression* aVal = dynamic_cast
			<const IntegerExpression*> (aEval);
	const IntegerExpression* bVal = dynamic_cast
			<const IntegerExpression*> (bEval);

	if (aVal != 0 && bVal != 0) {
		return new IntegerExpression(aVal->value * bVal->value);
	}

	return aEval->mult(bEval);
}


const std::string& MultiplicationExpression::getOperatorSymbol(void) const
{
	return operatorSymbol;
}


const std::string DivisionExpression::operatorSymbol = "/";
DivisionExpression::DivisionExpression(void)
{
}


DivisionExpression::DivisionExpression(const Expression* a, const Expression* b)
{
	this->a = a;
	this->b = b;
}


DivisionExpression::~DivisionExpression(void)
{
}


const Expression* DivisionExpression::evaluate(void) const
{
	const Expression* aEval = a->evaluate();
	const Expression* bEval = b->evaluate();

	return aEval->div(bEval);
}


const std::string& DivisionExpression::getOperatorSymbol(void) const
{
	return operatorSymbol;
}


const Expression* DivisionExpression::add(const Expression* summand) const
{
	using math::BigInteger;
	const IntegerExpression* intExpr =
			dynamic_cast<const IntegerExpression*> (summand);
	if (intExpr != 0) {
		const Expression* newNumerator = this->a->add(intExpr->mult(this->b));
		DivisionExpression* divExpr = new DivisionExpression(newNumerator, this->b);
		const Expression* expr = divExpr->evaluate();
		delete divExpr;
		return expr;
	}

	return Expression::add(summand);
}


const Expression* DivisionExpression::sub(const Expression* subtrahend) const
{
	using math::BigInteger;
	const IntegerExpression* intExpr =
			dynamic_cast<const IntegerExpression*> (subtrahend);
	if (intExpr != 0) {
		const Expression* newNumerator = this->a->sub(intExpr->mult(this->b));
		DivisionExpression* divExpr = new DivisionExpression(newNumerator, this->b);
		const Expression* expr = divExpr->evaluate();
		delete divExpr;
		return expr;
	}

	return Expression::sub(subtrahend);
}


const Expression* DivisionExpression::mult(const Expression* factor) const
{
	using math::BigInteger;
	const IntegerExpression* intExpr =
			dynamic_cast<const IntegerExpression*> (factor);
	if (intExpr != 0) {
		const Expression* newNumerator = this->a->mult(intExpr);
		DivisionExpression* divExpr = new DivisionExpression(newNumerator, this->b);
		const Expression* expr = divExpr->evaluate();
		delete divExpr;
		return expr;
	}

	const DivisionExpression* divExpr = dynamic_cast<const DivisionExpression*> (factor);
	if (divExpr != 0) {
		const Expression* numerator = this->a->mult(divExpr->a);
		const Expression* denominator = this->b->mult(divExpr->b);
		DivisionExpression* div = new DivisionExpression(numerator, denominator);
		const Expression* ret = div->evaluate();
		delete div;
		return ret;
	}

	return Expression::mult(factor);
}


const Expression* DivisionExpression::div(const Expression* dividend) const
{
	using math::BigInteger;
	const IntegerExpression* intExpr =
			dynamic_cast<const IntegerExpression*> (dividend);
	if (intExpr != 0) {
		const Expression* newDenominator = this->b->mult(intExpr);
		DivisionExpression* divExpr = new DivisionExpression(this->a, newDenominator);
		const Expression* expr = divExpr->evaluate();
		delete divExpr;
		return expr;
	}

	const DivisionExpression* divExpr = dynamic_cast<const DivisionExpression*> (dividend);
	if (divExpr != 0) {
		const Expression* numerator = this->a->mult(divExpr->b);
		const Expression* denominator = this->b->mult(divExpr->a);
		DivisionExpression* div = new DivisionExpression(numerator, denominator);
		const Expression* ret = div->evaluate();
		delete div;
		return ret;
	}

	return Expression::div(dividend);
}


const Expression* DivisionExpression::pow(const Expression* b) const
{
	using math::BigInteger;
	const IntegerExpression* intExpr =
			dynamic_cast<const IntegerExpression*> (b);

	if (intExpr != 0) {
		return new DivisionExpression(this->a->pow(b), this->b->pow(b));
	}

	const DivisionExpression* divExpr =
			dynamic_cast<const DivisionExpression*> (b);
	if (divExpr != 0) {
		const IntegerExpression* intNumerator =
						dynamic_cast<const IntegerExpression*> (divExpr->a);
		if (intNumerator == 0 || intNumerator->value != 1) {
			PowerExpression* powerExpr =
					new PowerExpression(
							this->pow(divExpr->a),
							new DivisionExpression(
									new IntegerExpression(1),
									divExpr->b
							)
					);
			const Expression* eval = powerExpr->evaluate();
			delete powerExpr;
			return eval;
		}
	}
	return new PowerExpression(this, b);
}


std::string DivisionExpression::toString(void) const
{
	return "DivisionExpression[" + a->toString() + " " +
			getOperatorSymbol() + " " + b->toString() + "]";
}


const std::string PowerExpression::operatorSymbol = "^";
PowerExpression::PowerExpression(void)
{
}


PowerExpression::PowerExpression(const Expression* a, const Expression* b)
{
	this->a = a;
	this->b = b;
}


PowerExpression::~PowerExpression(void)
{
}


const Expression* PowerExpression::evaluate(void) const
{
	const Expression* aEval = a->evaluate();
	const Expression* bEval = b->evaluate();

	return aEval->pow(bEval);
}


const std::string& PowerExpression::getOperatorSymbol(void) const
{
	return operatorSymbol;
}

